﻿using RestApp.BusinessLogic.BusinessModels;
using RestApp.BusinessLogic.Code;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace RestApp.BusinessLogic
{
    public class ReportsLogic
    {
        public BigReportDetails BigDetails(string login, int month, int year)
        {
            using (var context = new RestAppEntities())
            {
                var user = context.Users.FirstOrDefault(t => t.Login == login);
                if (user == null)
                {
                    throw new InvalidOperationException("User not found");
                }

                var monthFacts = user
                    .WorkGraphics
                    .Where(t => t.Month == month 
                        && t.Year == year
                        && t.FactOfWorks != null)
                    .SelectMany(t => t.FactOfWorks);
                List<WorkTimeItem> items = new List<WorkTimeItem>();
                foreach (var item in monthFacts)
                {
                    items.Add(new WorkTimeItem()
                        {
                            DateWork = new DateTime(item.WorkGraphic.Year.Value, item.WorkGraphic.Month.Value, item.WorkGraphic.Day.Value),
                            FactEndWork = item.TimeEnd,
                            FactPay = item.Payment.FactPay.Value,
                            FactStartWork = item.TimeStart,
                            GraphicEndWork = item.WorkGraphic.EndTime.Value,
                            GraphicStartWork = item.WorkGraphic.StartTime.Value,
                            IsReasondWorkCancelation = item.IsReasondForWorkCancelation.Value,
                            MustBePay = item.Payment.MustBePay.Value,
                            NoWork = item.NoWork.Value,
                            NoWorkComment = item.Comment,
                            PayComment = item.Payment.Comment,
                            Penalties = item.Payment.Penalties.Value
                        });
                }

                return new BigReportDetails()
                {
                    Items = new List<WorkTimeItem>(items),
                    Month = month,
                    Year = year
                };
            }
        }

        public IEnumerable<BigReportItem> GetBigUserReport(string login)
        {
            using (var context = new RestAppEntities())
            {
                var user = context.Users.FirstOrDefault(t => t.Login == login);
                if (user == null)
                {
                    throw new InvalidOperationException("User not found");
                }
                var facts = user.WorkGraphics.Where(t => t.FactOfWorks != null).ToList();
                var yearPayments = facts.GroupBy(t => t.Year)
                    .Select(t => new
                    {
                        year = t.Key.Value,
                        yearPayments = t
                    });
                //Dictionary<int, List<BigReportAmount>> reportItems = new Dictionary<int, List<BigReportAmount>>();
                List<BigReportItem> reportItems = new List<BigReportItem>();
                foreach (var item in yearPayments)
                {
                    var yearMonths = item.yearPayments.Select(t => t.Month).Distinct();
                    List<BigReportAmount> monthAmount = new List<BigReportAmount>();
                    foreach (var month in yearMonths)
                    {
                        var monthPayments = item.yearPayments.Where(t => t.Month == month && t.Year == item.year).ToList();
                        var factPayments = monthPayments.Where(t => t.FactOfWorks != null).SelectMany(t => t.FactOfWorks).ToList();
                        monthAmount.Add(new BigReportAmount()
                            {
                                Month = month.Value,
                                MonthAmount = factPayments.Where(x => x.Payment != null).Sum(x => x.Payment.FactPay).Value
                            });
                    }

                    reportItems.Add(new BigReportItem()
                    {
                        Year = item.year,
                        YearAmounts = new List<BigReportAmount>(monthAmount)
                    });
                }

                return reportItems;
            }
        }

        public WorkTimeReport GetWorkTimeReport(DateTime startPeriod, DateTime endPeriod, int idUser)
        {
            using (var context = new RestAppEntities())
            {
                var user = context.Users.FirstOrDefault(t => t.Id == idUser);
                if (user == null)
                {
                    throw new InvalidOperationException("User not found. Is is " + idUser);
                }
                WorkTimeFormatter formatter = new WorkTimeFormatter();

                WorkTimeReport report = new WorkTimeReport();
                var workItems = GetWorkTimeItems(user.WorkGraphics, startPeriod, endPeriod);
                report.WorkTimeItems = workItems;
                report.IdUser = user.Id;
                report.LastName = user.LastName;
                report.MiddleName = user.MiddleName;
                report.Name = user.FirstName;
                report.AmountFactPay = workItems.Sum(t => t.FactPay);
                report.AmountFactWorkHours = workItems
                    .Aggregate(new TimeSpan(0), (p, t) => p.Add(formatter.GetWorkTime(t.FactStartWork, t.FactEndWork)));
                report.AmountGraphicWorkHours = workItems
                    .Aggregate(new TimeSpan(0), (p, t) => p.Add(formatter.GetWorkTime(t.GraphicStartWork, t.GraphicEndWork)));
                report.AmountMustBePay = workItems.Sum(t => t.MustBePay);
                report.StartPeriod = startPeriod;
                report.EndPeriod = endPeriod;

                return report;
            }
        }

        private List<WorkTimeItem> GetWorkTimeItems(IEnumerable<WorkGraphic> allGraphics, DateTime startPeriod, DateTime endPeriod)
        {
            return GetGraphicsForPeriod(allGraphics, startPeriod, endPeriod)
                .Select(t => new WorkTimeItem()
                {
                    #region add items
                    DateWork = new DateTime(t.Year.Value, t.Month.Value, t.Day.Value),
                    FactEndWork = t.FactOfWorks != null && t.FactOfWorks.Any()
                        ? t.FactOfWorks.First().TimeEnd
                        : TimeSpan.Zero,
                    FactPay = t.FactOfWorks != null && t.FactOfWorks.Any() && t.FactOfWorks.First().Payment != null
                        ? t.FactOfWorks.First().Payment.FactPay.Value
                        : 0,
                    FactStartWork = t.FactOfWorks != null && t.FactOfWorks.Any()
                        ? t.FactOfWorks.First().TimeStart
                        : TimeSpan.Zero,
                    GraphicEndWork = t.EndTime.Value,
                    GraphicStartWork = t.StartTime.Value,
                    IsReasondWorkCancelation = t.FactOfWorks != null && t.FactOfWorks.Any()
                        ? t.FactOfWorks.First().IsReasondForWorkCancelation.Value
                        : false,
                    MustBePay = t.FactOfWorks != null && t.FactOfWorks.Any() && t.FactOfWorks.First().Payment != null
                        ? t.FactOfWorks.First().Payment.MustBePay.Value
                        : 0,
                    NoWork = t.FactOfWorks != null && t.FactOfWorks.Any()
                        ? t.FactOfWorks.First().NoWork.Value
                        : false,
                    NoWorkComment = t.FactOfWorks != null && t.FactOfWorks.Any()
                        ? t.FactOfWorks.First().Comment
                        : string.Empty,
                    PayComment = t.FactOfWorks != null && t.FactOfWorks.Any() && t.FactOfWorks.First().Payment != null
                        ? t.FactOfWorks.First().Payment.Comment
                        : string.Empty,
                    Penalties = t.FactOfWorks != null && t.FactOfWorks.Any() && t.FactOfWorks.First().Payment != null
                        ? t.FactOfWorks.First().Payment.Penalties.Value
                        : 0
                    #endregion
                })
                .ToList();
        }

        private List<WorkGraphic> GetGraphicsForPeriod(IEnumerable<WorkGraphic> allGraphics, DateTime startPeriod, DateTime endPeriod)
        {
            return allGraphics
                .Where(t => new DateTime(t.Year.Value, t.Month.Value, t.Day.Value) >= startPeriod
                    && new DateTime(t.Year.Value, t.Month.Value, t.Day.Value) <= endPeriod
                    && t.FactOfWorks != null && t.FactOfWorks.Any())
                .ToList();
        }
    }
}
